## Магический метод __call__. 


```python
class Counter:
    def __init__(self):
        self.__counter = 0
```
Мы можем создавать его экземпляры командой:

```python
c = Counter()
```

Обратите внимание на круглые скобки после имени класса. 
В общем случае – это оператор вызова, например, так можно вызывать функции. 
Но, как видите, так можно вызывать и классы. 

В действительности, когда происходит вызов класса, то автоматически запускается магический метод `__call__`и в данном случае он создает новый экземпляр этого класса

То есть, класс можно вызывать подобно функции благодаря встроенной для него реализации магического метода `__call__`. А вот экземпляры классов так вызывать уже нельзя. 

Если записать команду:
```
c()
```

мы можем поправить этот момент, если явно в классе Counter пропишем магический метод `__call__`, например, так

```python
class Counter:
    def __init__(self):
        self.__counter = 0
 
    def __call__(self, *args, **kwargs):
        print("__call__")
        self.__counter += 1
        return self.__counter
```

Здесь мы выводим сообщение, что был вызван данный метод, затем увеличиваем счетчик counter для текущего объекта на 1 и возвращаем его.

Запустим программу снова и теперь никаких ошибок нет, а в консоли отобразилась строка `__call__`, что означает вызов магического метода `__call__`. 
То есть, благодаря добавлению этого магического метода в наш класс, теперь можно вызывать его экземпляры подобно функциям через оператор круглые скобки. 

Классы, экземпляры которых можно вызывать подобно функциям, получили название функторы.

В нашем случае метод `__call__` возвращает значение счетчика, поэтому с объектом можно работать, следующим образом:

```python
c = Counter()
c2 = Counter()
c()
c()
res = c()
res2 = c2()
print(res, res2)
```

Давайте еще раз посмотрим на определение метода `__call__`.

Здесь записаны параметры *args, **kwargs. Это значит, что при вызове объектов мы можем передавать им произвольное количество аргументов. Например, в нашем случае можно указать значение изменения счетчика при текущем вызове. 

Для этого я перепишу метод __call__, следующим образом:

```python
    def __call__(self, step=1, *args, **kwargs):
        self.__counter += step
        return self.__counter

c(2)
c(10)
res = c()
res2 = c2(-5)

```